package com.datasecops.scannerservices.service;

import com.datasecops.scannerservices.entity.CommonParam;
import com.datasecops.scannerservices.entity.ScanCondition;
import com.datasecops.scannerservices.enumerate.DBType;
import com.datasecops.scannerservices.enumerate.EngineStatus;
import com.datasecops.scannerservices.operation.*;
import com.datasecops.scannerservices.util.AESEncoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

/**
 * 扫描流程服务
 *
 * @author: SunLingzhi
 * @time: 2022/8/18 9:46
 */
@Service
@Slf4j
public class ScanProcessServer {

    @Resource
    private DBCommonOperation dbCommonServer;

    @Resource
    private NoSqlDBCommonOperation noSqlDbCommonServer;

    @Resource
    private ScannerEngineServer engineServer;

    @Resource
    private RedisOperation reidsCommonOperation;

    @Resource
    private ESClientOperation demoCommonOperation;

    @Resource
    private HBaseOperation hBaseOperation;

    @Resource
    private OssOperation ossOperation;



    /**
     * 开始扫描流程
     *
     * @param
     * @param document
     * @param commonParam
     * @param scanJobId
     * @param jobName
     * @param scannerID
     * @return
     * @Description
     * @author SunLingzhi
     * @time 2022/8/18 9:47
     */
    public void startScanProcess(Document document, CommonParam commonParam, String scanJobId, String jobName, String scannerID) {
        Element rootElement = document.getRootElement();

        //扫描任务Id
        String jobId = rootElement.attributeValue("JobID");//取出JobID的值
            //扫描目标解析
        Element sqlScannerElem = rootElement.element("SQLScanner");//取得根节点下SQLScanner字节点

        if (null == sqlScannerElem) {
            log.error("Failed Method scanProcess sqlScannerDom is null");
            return;
        }

        ScanCondition scanCondition = new ScanCondition();

        //扫描条件
        Element advanceCtlElem = sqlScannerElem.element("AdvanceCtl");
        String advanceCtlStatus = advanceCtlElem.attributeValue("Status");
        if (advanceCtlStatus.equalsIgnoreCase("Enable")){//equalsIgnoreCase() 方法用于将字符串与指定的对象比较，不考虑大小写。
            //每分钟处理的最大行数
            Element restrictItemElem = advanceCtlElem.element("RestrictItem");
            String restrictItemStatus = restrictItemElem.attributeValue("Status");
            if (restrictItemStatus.equalsIgnoreCase("Enable")){
                String maxRosPerMin = restrictItemElem.attributeValue("MaxRosPerMin");
                if (StringUtils.isNotBlank(maxRosPerMin)){
                    scanCondition.setRestrictItem(Integer.parseInt(maxRosPerMin));
                }
            }

            //每张表匹配到的记录数
            Element tableMatchElem = advanceCtlElem.element("TableMatch");
            String tableMatchStatus = tableMatchElem.attributeValue("Status");
            if (tableMatchStatus.equalsIgnoreCase("Enable")){
                String matchThreshold = tableMatchElem.attributeValue("MatchThreshold");
                if (StringUtils.isNotBlank(matchThreshold)){
                    scanCondition.setTableMatch(Integer.parseInt(matchThreshold));
                }
            }
        }

        //过滤器
        Element pathFilterElem = sqlScannerElem.element("PathFilter");
        String pathFilterStatus = pathFilterElem.attributeValue("Status");
        if (pathFilterStatus.equalsIgnoreCase("Enable")){
            List<Element> elements = pathFilterElem.elements();
            for (Element e : elements) {
                String type = e.attributeValue("Type");
                String param = e.attributeValue("Parameter");
                if (type.equalsIgnoreCase("Include")){

                    scanCondition.setIncludeFilter(param);
                }else if(type.equalsIgnoreCase("Exclude")){
                    scanCondition.setExcludeFilter(param);
                }
            }
        }

        //扫描目标 --重点
        Element scanContentElem = sqlScannerElem.element("ScanContent");
        if (null == scanContentElem) {
            log.error("Failed Method scanProcess scanContentDom is null");
            return;
        }
        //>>>>>>>>>>1、调用扫描引擎--执行开始命令 ScannerEngine.exe
       /* engineServer.callScannerEngine(jobId, "scanproxy", EngineStatus.START.getValue(), null);*/

        //>>>>>>>>>>2、批次扫描数据库
        List<Element> scanElements = scanContentElem.elements();
        for (Element elem : scanElements) {
            String dbType = elem.attributeValue("VendorName");//数据库类型
            DBType typeEnum = DBType.getDBType(dbType);
            if (null == typeEnum) {
                log.error("This type of database is not supported dbType: " + dbType);
                continue;
            }
            if ("OSS".equalsIgnoreCase(typeEnum.name())){
                ossDatabaseScanning(jobId,typeEnum,elem,scanCondition,commonParam,jobName,scannerID);
            }else {
                //一个数据库
                databaseScanning(jobId,typeEnum,elem,scanCondition,commonParam,jobName,scannerID);
            }
        }

        //>>>>>>>>>>3、调用扫描引擎--执行结束命令
        engineServer.callScannerEngine(jobId, "scanproxy", EngineStatus.STOP.getValue(), null);
        log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>SCANNER END>>>>>>>>>>>>>>>>>>>>>>>>>>>");
    }

    /**
     * 批次扫描数据库
     *
     * @param
     * @param typeEnum
     * @param elem
     * @param scanCondition
     * @param commonParam
     * @param jobName
     * @param scannerID
     * @return
     * @Description
     * @author SunLingzhi
     * @time 2022/8/18 9:50
     */
    private void databaseScanning(String jobId, DBType typeEnum, Element elem, ScanCondition scanCondition,
                                  CommonParam commonParam, String jobName, String scannerID) {
        String ip = elem.attributeValue("DataSource");//数据库地址
        String portStr = elem.attributeValue("Port");//数据库端口
        String dbName = elem.attributeValue("Instance");//数据库名
        String Instance = elem.attributeValue("Instance");//数据库名
        String Model = elem.attributeValue("Model");//数据库模式
        String userName = elem.attributeValue("UserName");//用户名
        String password = elem.attributeValue("PasswordAES");//密码
        String maximumRowsStr = elem.attributeValue("MaximumRows");//每个表扫描的最大行数，为空则扫描全部

        //解密
        password = AESEncoder.DecryptV2WithKey(password);
        //Port
        int port = Integer.parseInt(portStr);
        //扫描限定行数，行内的优先级大于高级设置里面的
        Integer maximumRows = null;
        if (StringUtils.isNotBlank(maximumRowsStr)){
            maximumRows = Integer.parseInt(maximumRowsStr);
        }
        if (null == maximumRows || maximumRows <= 0){//行内未设置，检查高级设置
            if (null != scanCondition.getTableMatch() && scanCondition.getTableMatch() > 0){
                maximumRows = scanCondition.getTableMatch();
            }
        }

        //扫描排除过滤器
        Set<String> excludeTableNameSet = new HashSet<>();
        if (StringUtils.isNotBlank(scanCondition.getExcludeFilter())){
            try {
                String exclude = scanCondition.getExcludeFilter();
                String[] excludes = exclude.split(";");
                for (String s : excludes) {
                    String[] conf = s.split(":");
                    if (conf.length < 2){
                        continue;
                    }
                    if (typeEnum.name().equalsIgnoreCase(conf[0])){
                        String exName = conf[1].split("\\|")[1].split("\\*\\.")[1];
                        excludeTableNameSet.add(exName);
                    }
                }
            } catch (Exception e) {
                log.error("Failed Method databaseScanning:: parse excludeName error: " + e.getMessage());
            }
        }

        //一条扫描任务对应一个GUID
        String uuId = UUID.randomUUID().toString().toUpperCase();
        String type = String.valueOf(typeEnum);
        //HBase大小写敏感
        if (!type.equalsIgnoreCase(DBType.HBASE.name())){
            dbName = dbName.toUpperCase();
        }
        //神通数据库只有大写,没有小写
        if (type.equalsIgnoreCase(DBType.OSCAR.name())){
             Model= Model.toUpperCase();
             Instance = Instance.toUpperCase();
        }

        //扫描数据库
        CommonParam t1Param = new CommonParam(typeEnum, ip, port, userName, password, dbName,Instance);
        //扫描数据库下所有模式
        if (DBType.POSTGRESQL.name().equalsIgnoreCase(typeEnum.name()) || DBType.SQLSERVER.name().equalsIgnoreCase(typeEnum.name()) || type.equalsIgnoreCase(DBType.DB2.name()) || type.equalsIgnoreCase(DBType.RDS_POSTGRESQL.name()) || type.equalsIgnoreCase(DBType.RDS_SQLSERVER.name())){//有模式名的
            List<String> models = getModelsByDBType(type,t1Param,Instance,Model);
            for (String model : models) {
                //该数据库下的所有表
                String modeling = model.replace(" ","");//有的数据库模式会有空格
                List<String> tableNames = getTableNamesByDBType(type,t1Param,Instance,modeling);
                if (tableNames.size() == 0){
                    continue;
                }
                String sql;
                String str = String.valueOf(typeEnum);
                label: for (String tableName : tableNames) {
                    String newTableName = tableName.split("\\.")[0];//数据库的名字
                    String linage = tableName.split("\\.")[1];
                    //排除过滤器，名称匹配后跳过该表的扫描
                    if (excludeTableNameSet.size() > 0) {
                        for (String exName : excludeTableNameSet) {
                            boolean contains = exName.contains(newTableName);
                            if (contains) {
                                continue label;
                            }
                        }
                    }
                    sql = getSqlByDBType(str, dbName, Instance, modeling, newTableName);
                    CommonParam t2Param = new CommonParam(uuId, jobId, typeEnum, ip, port, userName, password, dbName, newTableName, sql, maximumRows, Instance, modeling);
                    boolean bResult = getDBResultByDBType(type, t2Param, Instance, commonParam, typeEnum,jobId,jobName,scannerID,linage);
                    log.info("Scanning DBName: " + dbName + " &TableName: " + newTableName + " &bResult: " + bResult);
                }
            }
        }else{//没有模式名的
            List<String> tableNames = getTableNamesByDBType(type,t1Param,Instance,Model);
            String sql;
            String str = String.valueOf(typeEnum);
            label: for (String tableName : tableNames) {
                String newTableName = tableName.split("\\.")[0];
                String linage = tableName.split("\\.")[1];
                //排除过滤器，名称匹配后跳过该表的扫描
                if (excludeTableNameSet.size() > 0){
                    for (String exName : excludeTableNameSet) {
                        boolean contains = exName.contains(newTableName);
                        if (contains){
                            continue label;
                        }
                    }
                }
                sql = getSqlByDBType(str,dbName,Instance,Model,newTableName);

                CommonParam t2Param = new CommonParam(uuId,jobId,typeEnum, ip, port, userName, password,dbName,newTableName,sql,maximumRows,Instance);
                boolean bResult = getDBResultByDBType(type,t2Param,Instance,commonParam, typeEnum, jobId, jobName,
                        scannerID, linage);
                log.info("Scanning DBName: " + dbName + " &TableName: " + newTableName + " &bResult: " + bResult);
            }
        }

        //3、部分数据库需要关闭连接
        //HBase大小写敏感
        if (type.equalsIgnoreCase(DBType.HBASE.name())){
            hBaseOperation.closeConnection(t1Param);
        }

    }


    /**
      * 获取数据库下的所有表名
      * @Description
      * @author SunLingzhi
      * @time 2022/10/25 15:33
      * @param
      * @return
      */
    private List<String> getTableNamesByDBType(String type,CommonParam t1Param, String instance, String model) {
        List<String> tableNames;

        if (type.equalsIgnoreCase(DBType.MONGODB.name())){
            tableNames = noSqlDbCommonServer.getTablesByDBName(t1Param,instance,model);
        }else if (type.equalsIgnoreCase(DBType.REDIS.name()) || type.equalsIgnoreCase(DBType.REDIS.name())){
            tableNames = reidsCommonOperation.getTablesByDBName(t1Param,instance,model);
        }else if (type.equalsIgnoreCase(DBType.ELASTICSEARCH.name())){
            tableNames = demoCommonOperation.getTablesByDBName(t1Param,instance);
        } else if(type.equalsIgnoreCase(DBType.HBASE.name())){
            tableNames = hBaseOperation.getTablesByDBName(t1Param,instance);
        }else {
            tableNames = dbCommonServer.getTablesByDBName(t1Param,instance,model);
        }
        return tableNames;
    }

     /**
      *扫描数据库下所有的模式名
      * @Description
      * @author GeXiaoLong
      * @time 2023/3/2 9:28
      * @param
      * @return
      */
    private List<String> getModelsByDBType(String type,CommonParam t1Param, String instance, String model) {
        List<String> tableNames = null;
       // if (type.equalsIgnoreCase(DBType.POSTGRESQL.name()) || type.equalsIgnoreCase(DBType.SQLSERVER.name()) || type.equalsIgnoreCase(DBType.DB2.name())){}
        tableNames = dbCommonServer.getModelByDBName(t1Param,instance,model);
        return tableNames;
    }


     /**
      * 获取查询语句根据数据库类型
      * @Description
      * @author SunLingzhi
      * @time 2022/10/25 15:39
      * @param
      * @param instance
      * @param model
      * @return
      */
    private String getSqlByDBType(String type,String dbName, String instance, String model, String tableName) {
        String sql;
        if(type.equalsIgnoreCase(DBType.POSTGRESQL.name())
                || type.equalsIgnoreCase(DBType.RDS_POSTGRESQL.name())
                || type.equalsIgnoreCase(DBType.DB2.name())
                ||type.equalsIgnoreCase(DBType.OPENGAUSS.name())
                ||type.equalsIgnoreCase(DBType.SQLSERVER.name())
                || type.equalsIgnoreCase(DBType.RDS_SQLSERVER.name())){
            sql = "SELECT * FROM " + model + "." + "\"" + tableName + "\"";
        }else if (type.equalsIgnoreCase(DBType.MONGODB.name())){
            sql = "db."  + tableName + ".find";
        }/*else if (type.equalsIgnoreCase("db")){
            sql = "SELECT * FROM " + instance + "." + tableName;
        }*/ else if (type.equalsIgnoreCase(DBType.OSCAR.name())){
            sql = "SELECT * FROM " + "\"" + model + "\"" + "." + "\"" + tableName + "\"";
        }else if (type.equalsIgnoreCase(DBType.DM.name()) || type.equalsIgnoreCase(DBType.ORACLE.name())){
            sql = "SELECT * FROM " + dbName + "." + "\"" + tableName + "\"" ;
        } else {
            sql = "SELECT * FROM " + dbName + "." + tableName;
        }
        return sql;
    }

     /**
      * 获取处理返回数据根据数据库类型
      * @Description
      * @author SunLingzhi
      * @time 2022/10/25 16:01
      * @param
      * @param commonParam
      * @param typeEnum
      * @param jobId
      * @param jobName
      * @param scannerID
      * @param linage
      * @return
      */
     private boolean getDBResultByDBType(String type, CommonParam t2Param, String instance, CommonParam commonParam,
                                         DBType typeEnum, String jobId, String jobName, String scannerID, String linage) {
         boolean bResult;
         if (type.equalsIgnoreCase(DBType.MONGODB.name())){
             bResult = noSqlDbCommonServer.getDBResult(t2Param,instance,commonParam,typeEnum,jobId,jobName,scannerID,linage);
         }else if (type.equalsIgnoreCase(DBType.REDIS.name()) || type.equalsIgnoreCase(DBType.RDS_REDIS.name().replace("_","-"))){
             bResult = reidsCommonOperation.getDBResult(t2Param,instance,commonParam,jobId,jobName,scannerID,linage);
         }else if (type.equalsIgnoreCase(DBType.ELASTICSEARCH.name())){
             bResult =demoCommonOperation.getDBResult(t2Param,commonParam,instance,jobId,jobName,scannerID,linage);
         }else if(type.equalsIgnoreCase(DBType.HBASE.name())){
             bResult = hBaseOperation.getDBResult(t2Param,commonParam,typeEnum,jobId,jobName,scannerID,linage);
         } else {
             bResult = dbCommonServer.getDBResult(t2Param,commonParam,typeEnum,jobId,jobName,scannerID,linage);
         }
         return bResult;
     }
    private void ossDatabaseScanning(String jobId, DBType typeEnum, Element elem, ScanCondition scanCondition,
                                     CommonParam commonParam, String jobName, String scannerID){
        String endPoInt = elem.attributeValue("DataSource");//外网访问节点
        String portStr = elem.attributeValue("Port");//数据库端口
        String buketName = elem.attributeValue("Instance");//存储空间名称
        String keyPreFix = elem.attributeValue("Model");//指定前缀,扫描此空间下所有的文件
        String accessKeyId = elem.attributeValue("UserName");//访问秘钥
        String accessKeySecret = elem.attributeValue("PasswordAES");//加密签名字符串和OSS用来验证签名字符串的密钥
        String maximumRowsStr = elem.attributeValue("MaximumRows");//每个表扫描的最大行数，为空则扫描全部

        //解密
        accessKeySecret = AESEncoder.DecryptV2WithKey(accessKeySecret);

        String uuId = UUID.randomUUID().toString().toUpperCase();

        CommonParam t2Param = new CommonParam(uuId,jobId,typeEnum, endPoInt,  accessKeyId, accessKeySecret,buketName,keyPreFix);
        //OssOperation ossOperation = new OssOperation();
        ossOperation.enumerate(endPoInt,buketName,keyPreFix,accessKeyId,accessKeySecret,t2Param,commonParam,jobId,jobName,scannerID);
    }

    public static void main(String[] args) {
        System.out.println(DBType.RDS_SQLSERVER);
        String ss  = "sdasd    ";
        ss = ss.replace(" ","");
        System.out.println("值"+ss+"00");

    }
}
